import React, { useState, useRef, useEffect } from "react";
import { Upload, Button, Progress, message, Card } from "antd";
import {
  UploadOutlined,
  PauseOutlined,
  CaretRightOutlined,
} from "@ant-design/icons";
import SparkMD5 from "spark-md5";

// 文件切片大小 (2MB)
const CHUNK_SIZE = 2 * 1024 * 1024;

// 并发请求数
const CONCURRENT_LIMIT = 4;

// 模拟上传延迟 (毫秒)
const UPLOAD_DELAY = 300;

interface Chunk {
  index: number;
  start: number;
  end: number;
  hash: string;
  file: Blob;
  status: "pending" | "uploading" | "completed" | "error";
}

const Index: React.FC = () => {
  // 定义文件状态
  const [aa, setAa] = useState("aa565");
  const [file, setFile] = useState<File | null>(null);
  // 定义文件哈希计算进度
  const [hashProgress, setHashProgress] = useState(0);
  // 定义文件上传进度
  const [uploadProgress, setUploadProgress] = useState(0);
  // 定义是否暂停上传
  const [isPaused, setIsPaused] = useState(false);
  // 定义是否正在上传
  const [isUploading, setIsUploading] = useState(false);
  // 定义已上传文件列表
  const [uploadedFiles, setUploadedFiles] = useState<string[]>([]);
  // 定义切片数组
  const chunksRef = useRef<Chunk[]>([]);
  // 定义文件哈希
  const fileHashRef = useRef<string>("");
  // 定义已上传切片数组
  const uploadedChunksRef = useRef<number[]>([]);
  //   // 定义AbortController
  //   const controllerRef = useRef<AbortController>(new AbortController());
  // 定义IndexedDB数据库
  const dbRef = useRef<IDBDatabase | null>(null);

  // 初始化 IndexedDB
  useEffect(() => {
    // 打开数据库连接
    const request = indexedDB.open("FileUploadDB", 1);
    // 数据库版本发生变化时触发
    request.onupgradeneeded = (event) => {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      const db = (event.target as IDBOpenDBRequest).result;
      // 创建文件对象存储
      if (!db.objectStoreNames.contains("files")) {
        db.createObjectStore("files", { keyPath: "hash" });
      }
      // 创建切片对象存储
      if (!db.objectStoreNames.contains("chunks")) {
        db.createObjectStore("chunks", { keyPath: "id" });
      }
    };
    // 数据库打开成功时触发
    request.onsuccess = (event) => {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      dbRef.current = (event.target as IDBOpenDBRequest).result;
      // 从数据库加载已上传文件列表
      loadUploadedFiles();
    };
    // 数据库打开失败时触发
    request.onerror = (event) => {
      console.error("数据库打开失败", event);
    };
  }, []);

  // 从数据库加载已上传文件列表
  const loadUploadedFiles = () => {
    if (!dbRef.current) return;

    const transaction = dbRef.current.transaction(["files"], "readonly");
    const store = transaction.objectStore("files");
    const request = store.getAll();

    request.onsuccess = () => {
      const files = request.result.map((f: any) => f.name);
      setUploadedFiles(files);
    };
  };

  // 生成文件哈希 (内容哈希)
  const calculateFileHash = (file: File): Promise<string> => {
    return new Promise((resolve) => {
      // 小文件直接计算MD5
      if (file.size <= CHUNK_SIZE) {
        // 直接读取文件内容并计算MD5
        const fileReader = new FileReader();
        // 读取文件内容并计算MD5
        fileReader.onload = (e) => {
          const spark = new SparkMD5.ArrayBuffer();
          spark.append(e.target?.result as ArrayBuffer);
          setHashProgress(100);
          resolve(spark.end());
        };
        // 读取文件内容
        fileReader.readAsArrayBuffer(file);
        return;
      }

      // 大文件增量计算
      const blobSlice = File.prototype.slice;
      // 切片数量
      const chunks = Math.ceil(file.size / CHUNK_SIZE);
      // 当前切片索引
      let currentChunk = 0;
      // 初始化SparkMD5实例
      const spark = new SparkMD5.ArrayBuffer();
      // 读取切片并计算MD5
      const fileReader = new FileReader();
      // 读取切片并计算MD5
      fileReader.onload = (e) => {
        spark.append(e.target?.result as ArrayBuffer);
        currentChunk++;
        // 计算进度
        setHashProgress(Math.floor((currentChunk / chunks) * 100));
        // 全部切片计算完毕
        if (currentChunk < chunks) {
          // 读取下一个切片
          loadNext();
        } else {
          // 全部切片计算完毕，保存文件哈希
          resolve(spark.end());
        }
      };
      // 读取切片失败
      fileReader.onerror = () => {
        message.error("文件读取失败");
      };

      // 加载下一个切片
      const loadNext = () => {
        // 读取当前切片
        const start = currentChunk * CHUNK_SIZE;
        // 计算当前切片结束位置
        const end = Math.min(start + CHUNK_SIZE, file.size);
        // 读取切片并计算MD5
        fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));
      };
      // 开始读取第一个切片
      loadNext();
    });
  };

  // 创建文件切片 (仅当文件大于2MB时切片)
  const createFileChunks = (file: File, hash: string): Chunk[] => {
    if (file.size <= CHUNK_SIZE) {
      // 小文件不切片
      return [
        {
          index: 0,
          start: 0,
          end: file.size,
          hash,
          file,
          status: "pending",
        },
      ];
    }

    // 大文件切片
    const chunks: Chunk[] = [];
    let start = 0;
    let index = 0;
    
    while (start < file.size) {
      const end = Math.min(start + CHUNK_SIZE, file.size);
      const chunk = file.slice(start, end);
      chunks.push({
        index,
        start,
        end,
        hash: `${hash}-${index}`,
        file: chunk,
        status: "pending",
      });
      start = end;
      index++;
    }

    return chunks;
  };

  // 检查文件是否存在 (秒传)
  const checkFileExist = async (hash: string): Promise<boolean> => {
    // 检查文件是否已上传
    return new Promise((resolve) => {
      // 检查数据库是否打开
      if (!dbRef.current) {
        // 数据库未打开，直接返回不存在
        resolve(false);

        return;
      }
      // 从数据库获取文件信息
      const transaction = dbRef.current.transaction(["files"], "readonly");
      // 从数据库获取文件信息
      const store = transaction.objectStore("files");
      // 从数据库获取文件信息
      const request = store.get(hash);
      // 从数据库获取文件信息
      request.onsuccess = () => {
        
        resolve(!!request.result);
      };
      // 从数据库获取文件信息失败
      request.onerror = () => {
        // 数据库打开失败
        resolve(false);
      };
    });
  };

  // 获取已上传的切片 (断点续传)
  const getUploadedChunks = async (hash: string): Promise<number[]> => {
    // 从数据库获取已上传的切片
    return new Promise((resolve) => {
      // 检查数据库是否打开
      if (!dbRef.current) {
        // 数据库未打开，直接返回空数组
        resolve([]);
        return;
      }

      const transaction = dbRef.current.transaction(["chunks"], "readonly");
      const store = transaction.objectStore("chunks");
      const request = store.getAll();

      request.onsuccess = () => {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const chunks = request.result.filter((c: any) => c.fileHash === hash);
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        resolve(chunks.map((c: any) => c.index));
      };

      request.onerror = () => {
        resolve([]);
      };
    });
  };

  // 保存切片上传状态到数据库
  const saveChunkStatus = (chunk: Chunk) => {
    // 保存切片上传状态到数据库
    if (!dbRef.current) return;
    const transaction = dbRef.current.transaction(["chunks"], "readwrite");
    const store = transaction.objectStore("chunks");
    
    store.put({
      id: chunk.hash,
      index: chunk.index,
      fileHash: fileHashRef.current,
      status: chunk.status,
    });
  };

  // 保存文件上传状态到数据库
  const saveFileStatus = (file: File, hash: string) => {
    if (!dbRef.current) return;

    const transaction = dbRef.current.transaction(["files"], "readwrite");
    const store = transaction.objectStore("files");

    store.put({
      hash,
      name: file.name,
      size: file.size,
      uploadedAt: new Date().toISOString(),
    });

    // 更新已上传文件列表
    setUploadedFiles((prev) => [...prev, file.name]);
  };

  // 模拟上传单个文件 (小文件直接上传)
  const uploadSingleFile = async (file: File, hash: string) => {
    return new Promise<void>((resolve) => {
      let progress = 0;
      const interval = setInterval(() => {
        progress += 10;
        setUploadProgress(progress);

        if (progress >= 100) {
          clearInterval(interval);
          // 保存文件上传状态
          saveFileStatus(file, hash);
          resolve();
        }
      }, UPLOAD_DELAY / 10);
    });
  };

  // 模拟上传切片 (大文件)
  const uploadChunks = async (chunks: Chunk[], uploadedChunks: number[]) => {
    const needUploadChunks = chunks.filter(
      (chunk) => !uploadedChunks.includes(chunk.index)
    );

    const total = chunks.length;
    let uploadedCount = uploadedChunks.length;
    setUploadProgress(Math.floor((uploadedCount / total) * 100));

    const uploadChunk = async (chunk: Chunk) => {
      return new Promise<void>((resolve, reject) => {
        // 更新切片状态为上传中
        chunk.status = "uploading";
        chunksRef.current = [...chunksRef.current];

        // 模拟上传延迟
        setTimeout(() => {
          if (Math.random() > 0.1) {
            // 90%成功率
            // 更新切片状态为已完成
            chunk.status = "completed";
            chunksRef.current = [...chunksRef.current];

            uploadedCount++;
            setUploadProgress(Math.floor((uploadedCount / total) * 100));

            // 保存切片状态到数据库
            saveChunkStatus(chunk);
            resolve();
          } else {
            // 模拟上传失败
            chunk.status = "error";
            chunksRef.current = [...chunksRef.current];
            message.error(`切片 ${chunk.index + 1}/${chunks.length} 上传失败`);
            reject(new Error("上传失败"));
          }
        }, UPLOAD_DELAY);
      });
    };

    // 并发控制
    const pool: Promise<void>[] = [];
    for (let i = 0; i < needUploadChunks.length; i++) {
      const chunk = needUploadChunks[i];

      // 如果暂停，则停止上传
      if (isPaused) break;

      const task = uploadChunk(chunk)
        .then(() => {
          // 任务完成后从池中移除
          pool.splice(pool.indexOf(task), 1);
        })
        .catch(() => {
          pool.splice(pool.indexOf(task), 1);
        });

      pool.push(task);

      if (pool.length >= CONCURRENT_LIMIT) {
        await Promise.race(pool);
      }
    }

    await Promise.allSettled(pool);
  };

  // 模拟合并切片 (仅大文件需要)
  const mergeChunks = async (file: File, hash: string) => {
    return new Promise<void>((resolve) => {
      // 模拟合并延迟
      setTimeout(() => {
        // 保存文件上传状态
        saveFileStatus(file, hash);
        resolve();
      }, UPLOAD_DELAY);
    });
  };

  // 处理上传
  const handleUpload = async () => {
    if (!file) return;

    try {
      setIsUploading(true);
      setIsPaused(false);

      // 1. 计算文件哈希
      const hash = await calculateFileHash(file);
      fileHashRef.current = hash;

      // 2. 检查文件是否已存在 (秒传)
      const fileExist = await checkFileExist(hash);
      if (fileExist) {
        message.success("文件秒传成功");
        setUploadProgress(100);
        return;
      }

      // 3. 创建切片 (小文件不切片)
      const chunks = createFileChunks(file, hash);
      chunksRef.current = chunks;

      // 4. 获取已上传的切片 (断点续传)
      const uploadedChunks = await getUploadedChunks(hash);
      uploadedChunksRef.current = uploadedChunks;

      // 5. 上传文件 (区分大小文件)
      if (file.size <= CHUNK_SIZE) {
        // 小文件直接上传
        await uploadSingleFile(file, hash);
      } else {
        // 大文件切片上传
        await uploadChunks(chunks, uploadedChunks);
        // 6. 合并切片 (仅大文件需要)
        await mergeChunks(file, hash);
      }

      message.success("文件上传成功");
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
    } catch (error: any) {
      if (!isPaused) {
        message.error("文件上传失败: " + error.message);
      }
    } finally {
      if (!isPaused) {
        setIsUploading(false);
      }
    }
  };

  // 暂停上传
  const handlePause = () => {
    setIsPaused(true);
  };

  // 继续上传
  const handleResume = () => {
    setIsPaused(false);
    handleUpload();
  };

  // 重置状态
  const reset = () => {
    setFile(null);
    setHashProgress(0);
    setUploadProgress(0);
    chunksRef.current = [];
    uploadedChunksRef.current = [];
    fileHashRef.current = "";
    setIsPaused(false);
    setIsUploading(false);
  };

  // 选择文件回调
  const beforeUpload = (file: File) => {
    reset();
    setFile(file);
    return false; // 阻止自动上传
  };

  // 渲染切片状态
  const renderChunkStatus = (chunk: Chunk) => {
    const statusMap: Record<string, string> = {
      pending: "等待上传",
      uploading: "上传中",
      completed: "已完成",
      error: "失败",
    };

    const colorMap: Record<string, string> = {
      pending: "#8c8c8c",
      uploading: "#1890ff",
      completed: "#52c41a",
      error: "#ff4d4f",
    };

    return (
      <div key={chunk.index} style={{ marginBottom: 8 }}>
        <div style={{ display: "flex", justifyContent: "space-between" }}>
          <span>
            切片 #{chunk.index + 1} ({Math.round(chunk.file.size / 1024)} KB)
          </span>
          <span style={{ color: colorMap[chunk.status] }}>
            {statusMap[chunk.status]}
          </span>
        </div>
        <Progress
          percent={chunk.status === "completed" ? 100 : 0}
          showInfo={false}
          strokeColor={colorMap[chunk.status]}
        />
      </div>
    );
  };

  return (
    <div style={{ padding: 20, maxWidth: 800, margin: "0 auto" }}>
      <h1 style={{ textAlign: "center", marginBottom: 24 }}>
        大文件上传解决方案
      </h1>

      <Card title="文件上传" style={{ marginBottom: 24 }}>
        <Upload
          beforeUpload={beforeUpload}
          showUploadList={false}
          disabled={isUploading && !isPaused}
        >
          <Button icon={<UploadOutlined />} size="large">
            选择文件
          </Button>
        </Upload>

        {file && (
          <div style={{ marginTop: 24 }}>
            <div style={{ marginBottom: 16 }}>
              <div>
                <strong>文件名:</strong> {file.name}
              </div>
              <div>
                <strong>文件大小:</strong>{" "}
                {(file.size / 1024 / 1024).toFixed(2)} MB
              </div>
            </div>

            {hashProgress > 0 && hashProgress < 100 && (
              <div style={{ marginBottom: 16 }}>
                <div>
                  <strong>哈希计算进度:</strong>
                </div>
                <Progress percent={hashProgress} status="active" />
              </div>
            )}

            {uploadProgress > 0 && (
              <div style={{ marginBottom: 16 }}>
                <div>
                  <strong>上传进度:</strong>
                </div>
                <Progress percent={uploadProgress} />
              </div>
            )}

            {chunksRef.current.length > 0 && file.size > CHUNK_SIZE && (
              <div style={{ marginBottom: 16 }}>
                <div>
                  <strong>切片状态 ({chunksRef.current.length}个):</strong>
                </div>
                <div
                  style={{ maxHeight: 200, overflowY: "auto", marginTop: 8 }}
                >
                  {chunksRef.current.map(renderChunkStatus)}
                </div>
              </div>
            )}

            <div style={{ display: "flex", gap: 8, marginTop: 16 }}>
              {!isUploading ? (
                <Button
                  type="primary"
                  onClick={handleUpload}
                  icon={<UploadOutlined />}
                  disabled={isPaused}
                >
                  开始上传
                </Button>
              ) : isPaused ? (
                <Button
                  type="primary"
                  onClick={handleResume}
                  icon={<CaretRightOutlined />}
                >
                  继续上传
                </Button>
              ) : (
                <Button danger onClick={handlePause} icon={<PauseOutlined />}>
                  暂停上传
                </Button>
              )}

              <Button onClick={reset} disabled={isUploading && !isPaused}>
                重置
              </Button>
            </div>
          </div>
        )}
      </Card>

      <Card title="已上传文件">
        {uploadedFiles.length === 0 ? (
          <div style={{ textAlign: "center", padding: 16, color: "#8c8c8c" }}>
            暂无已上传文件
          </div>
        ) : (
          <div style={{ maxHeight: 200, overflowY: "auto" }}>
            {uploadedFiles.map((fileName, index) => (
              <div
                key={index}
                style={{
                  padding: 8,
                  borderBottom: "1px solid #f0f0f0",
                  display: "flex",
                  alignItems: "center",
                }}
              >
                <span style={{ marginRight: 8 }}>✓</span>
                {fileName}
              </div>
            ))}
          </div>
        )}
      </Card>

      <div style={{ marginTop: 24, textAlign: "center", color: "#8c8c8c" }}>
        <p>此演示使用浏览器端 IndexedDB 模拟文件上传功能</p>
        <p>支持大文件切片上传、断点续传和秒传功能</p>
      </div>
    </div>
  );
};

export default Index;
